package cn.tworice.common.util.cryption;

public class SM3Utils {
    /**
     * 常量
     */
    private static final int[] T = new int[64];
    private static final int[] IV = {
            0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
            0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
    };

    static {
        for (int i = 0; i < 16; i++) {
            T[i] = 0x79CC4519;
        }
        for (int i = 16; i < 64; i++) {
            T[i] = 0x7A879D8A;
        }
    }

    /**
     * 循环左移
     */
    private static int leftRotate(int x, int n) {
        return (x << n) | (x >>> (32 - n));
    }

    /**
     * FF函数
     */
    private static int FF(int x, int y, int z, int j) {
        if (j >= 0 && j <= 15) {
            return x ^ y ^ z;
        } else {
            return (x & y) | (x & z) | (y & z);
        }
    }

    /**
     * GG函数
     */
    private static int GG(int x, int y, int z, int j) {
        if (j >= 0 && j <= 15) {
            return x ^ y ^ z;
        } else {
            return (x & y) | (~x & z);
        }
    }

    /**
     * P0置换
     */
    private static int P0(int x) {
        return x ^ leftRotate(x, 9) ^ leftRotate(x, 17);
    }

    /**
     * P1置换
     */
    private static int P1(int x) {
        return x ^ leftRotate(x, 15) ^ leftRotate(x, 23);
    }

    /**
     * 压缩函数
     */
    private static void CF(int[] V, int[] B) {
        int[] W = new int[68];
        int[] W1 = new int[64];

        for (int i = 0; i < 16; i++) {
            W[i] = B[i];
        }

        for (int i = 16; i < 68; i++) {
            W[i] = P1(W[i - 16] ^ W[i - 9] ^ leftRotate(W[i - 3], 15))
                    ^ leftRotate(W[i - 13], 7) ^ W[i - 6];
        }

        for (int i = 0; i < 64; i++) {
            W1[i] = W[i] ^ W[i + 4];
        }

        int[] reg = copyArray(V);

        for (int j = 0; j < 64; j++) {
            int SS1 = leftRotate(leftRotate(reg[0], 12) + reg[4] + leftRotate(T[j], j), 7);
            int SS2 = SS1 ^ leftRotate(reg[0], 12);
            int TT1 = FF(reg[0], reg[1], reg[2], j) + reg[3] + SS2 + W1[j];
            int TT2 = GG(reg[4], reg[5], reg[6], j) + reg[7] + SS1 + W[j];
            reg[3] = reg[2];
            reg[2] = leftRotate(reg[1], 9);
            reg[1] = reg[0];
            reg[0] = TT1;
            reg[7] = reg[6];
            reg[6] = leftRotate(reg[5], 19);
            reg[5] = reg[4];
            reg[4] = P0(TT2);
        }

        for (int i = 0; i < 8; i++) {
            V[i] ^= reg[i];
        }
    }

    /**
     * 手动实现数组复制
     */
    private static int[] copyArray(int[] source) {
        int[] destination = new int[source.length];
        for (int i = 0; i < source.length; i++) {
            destination[i] = source[i];
        }
        return destination;
    }

    /**
     * 填充消息
     */
    private static byte[] pad(byte[] data) {
        int len = data.length;
        int k = 64 - ((len + 9) % 64);
        byte[] padding = new byte[k + 9];
        padding[0] = (byte) 0x80;

        long lengthInBits = len * 8L;
        for (int i = 0; i < 8; i++) {
            padding[k + 1 + i] = (byte) (lengthInBits >>> (56 - i * 8));
        }

        byte[] padded = new byte[len + padding.length];
        System.arraycopy(data, 0, padded, 0, len);
        System.arraycopy(padding, 0, padded, len, padding.length);

        return padded;
    }

    /**
     * 计算哈希值
     */
    public static byte[] hash(byte[] data) {
        byte[] padded = pad(data);
        int[] V = copyArray(IV);
        int n = padded.length / 64;

        for (int i = 0; i < n; i++) {
            int[] B = new int[16];
            for (int j = 0; j < 16; j++) {
                B[j] = ((padded[i * 64 + j * 4] & 0xFF) << 24)
                        | ((padded[i * 64 + j * 4 + 1] & 0xFF) << 16)
                        | ((padded[i * 64 + j * 4 + 2] & 0xFF) << 8)
                        | (padded[i * 64 + j * 4 + 3] & 0xFF);
            }
            CF(V, B);
        }

        byte[] hash = new byte[V.length * 4];
        for (int i = 0; i < V.length; i++) {
            hash[i * 4] = (byte) (V[i] >>> 24);
            hash[i * 4 + 1] = (byte) (V[i] >>> 16);
            hash[i * 4 + 2] = (byte) (V[i] >>> 8);
            hash[i * 4 + 3] = (byte) V[i];
        }
        return hash;
    }

    /**
     * 将字节数组转换为十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * 对输入字符串进行哈希处理
     */
    public static String hash(String input) {
        byte[] hash = hash(input.getBytes());
        return bytesToHex(hash);
    }

    public static void main(String[] args) {
        // 测试
        String password = "my_secure_password";
        String hashedPassword = SM3Utils.hash(password);
        System.out.println("原始密码: " + password);
        System.out.println("哈希值: " + hashedPassword);
    }
}
